package memorphic.utils
{
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.TimerEvent;
	import flash.net.LocalConnection;
	import flash.utils.ByteArray;
	import flash.utils.Proxy;
	import flash.utils.Timer;
	import flash.utils.flash_proxy;
	
	import mx.logging.Log;
	
	use namespace flash_proxy
	

	
	dynamic public class AVM1Loader extends Proxy implements IEventDispatcher
	{
		public static const BASEID:String = "_FlashProxy";
		
		private var __ldr:Loader;
		
		[Embed(source="../../proxy.swf", mimeType="application/octet-stream")]
		private var SWFBytes:Class;
		
		private var __timer:Timer;
		private var __lcCreated:Boolean = false;
		private var in_lc:LocalConnection;
		private var out_lc:LocalConnection;
				
		private var LC_NAME_BASE:String = BASEID;
		
		private var FLASH_CONNECT:String;
		private var FLEX_CONNECT:String;
		
		private var __targetURL:String;
		private var __availible:Boolean = false;
		private var __dispatcher:EventDispatcher;
		
		private var retryTimer:Timer;
		
		public function AVM1Loader(pSource:String = "")
		{
			__dispatcher = new EventDispatcher(this);
			source = pSource;
			retryTimer = new Timer(50, 1);
			init();
		}
		public function hasEventListener(type:String):Boolean
		{
			return __dispatcher.hasEventListener(type);
		}
		
		public function willTrigger(type:String):Boolean
		{
			return __dispatcher.willTrigger(type);;
		}
		
		public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0.0, useWeakReference:Boolean=false):void
		{
			__dispatcher.addEventListener(type,listener,useCapture,priority,useWeakReference);
		}
		
		public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
		{
			__dispatcher.removeEventListener(type,listener,useCapture);
		}
		
		public function dispatchEvent(event:Event):Boolean
		{
			return __dispatcher.dispatchEvent(event);
		}
		
		public function set source(v:String):void
		{
			__targetURL = v;
		}
		public function get source():String
		{
			return __targetURL;
		}
		public function get content():Loader
		{
			if(!__ldr){
				__ldr = new Loader();
			}
			return __ldr;
		}
		public function init():void
		{
			if(!__lcCreated)
			{
				var bt:ByteArray = new SWFBytes()
				setLCID(bt);				
				FLASH_CONNECT = LC_NAME_BASE + "_IN";
				FLEX_CONNECT = LC_NAME_BASE + "_OUT";
				
				if(!out_lc){
					out_lc = new LocalConnection();
					in_lc = new LocalConnection();
					in_lc.client = this;
				}
				
				try {
					in_lc.connect(FLEX_CONNECT);
					__lcCreated = true;
				}catch(error:ArgumentError){
//					Log.getLogger(LogType.DATA).error("AVM1Loader could not connect because the connection existed already. Retrying...");
					retryTimer.addEventListener(TimerEvent.TIMER, function(e:TimerEvent):void{
						init();
					});
					retryTimer.reset();
					retryTimer.start();
					// if there is another connection, try to disconnect it
					out_lc.send(FLEX_CONNECT, "remoteDisconnect");
				}			
				content.contentLoaderInfo.addEventListener(Event.COMPLETE,onProxyLoaded);
				content.loadBytes(bt);
				
			}
		}
		override flash_proxy  function callProperty(name:*, ...rest):* {
	    	var s:String = name.localName;
	    	var n:Array = new Array(FLASH_CONNECT, "resolve" ,s);
	    	n.splice(n.length,0,rest);
	    	out_lc.send.apply(out_lc,n);
	    }
	    override flash_proxy  function getProperty(name:*):* {
	      	 var p:*  = function():*
	         {
	         	callProperty(name,arguments);
	         }
	         return p;	        
	    }
		private function setLCID(b1:ByteArray):void
		{
			for(var i:uint = 0 ; i < b1.length -1 ; i++)
			{
				var tbt:ByteArray = new ByteArray();
				b1.position = i;
				var max:uint = ((b1.position + LC_NAME_BASE.length)> b1.length)? (b1.length - b1.position) : LC_NAME_BASE.length;
				b1.readBytes(tbt,0,max);
				var str:String = ""
				for(var p:uint = 0 ; p < tbt.length ; p++)
				{
					str += String.fromCharCode(tbt.readByte());
				}
				if(LC_NAME_BASE == str)
				{
					var socketID:String = getID();
					b1.position = i;
					//var start:ByteArray = getByteArray(b1,0,i-1);
					var tmparr:Array = LC_NAME_BASE.split("")
					for(var n :uint = 0 ; n < socketID.length ; n++)
					{
						tmparr[n]= socketID.charAt(n)
						b1.writeByte(socketID.charCodeAt(n));
					}
					LC_NAME_BASE = tmparr.join("");
					return;
				}
			}
		}
		//callback from the LC
		public function onTargetLoaded():void
		{
			__availible = true;
			dispatchEvent(new Event(Event.COMPLETE));
			
		}
		//Callback from the LC
		public function ping():void
		{
			__timer.removeEventListener(TimerEvent.TIMER,pingInner);
			__timer.stop();
			loadTarget();
		}
		
		// This is required in rare circumstances, where two connections are somehow made with the same id. The second connection
		// will then tell the first to disconnect
		public function remoteDisconnect():void
		{
			in_lc.close();
		}
		
		private function loadTarget():void
		{
			if(__targetURL)
			{
				out_lc.send(FLASH_CONNECT, "resolve" ,"loadMovie",__targetURL ,"GET");//" + __targetURL + "				
			}
		}
		private function pingInner(evt:TimerEvent):void
		{
			out_lc.send(FLASH_CONNECT,"ping");
		}
		
		private function onProxyLoaded(evt:Event):void
		{
			
			__timer = new Timer(500,0);
			__timer.addEventListener(TimerEvent.TIMER,pingInner);
			__timer.start();
			
			
		}
		
		public static function getID():String
		{
			var d:Date = new Date();
			var milli:String = Math.random().toString()+ d.hours.toString() + d.minutes.toString() + d.seconds.toString() + d.milliseconds.toString();
			if(milli.length > BASEID.length)
			{
				milli = milli.substr(milli.length -10,milli.length);
			}
			return milli;
		}

	}
}